window.module = {};

(function main(global, module, isWorker, workerSize) {
    var canUseWorker = !!(
        global.Worker &&
        global.Blob &&
        global.Promise &&
        global.OffscreenCanvas &&
        global.OffscreenCanvasRenderingContext2D &&
        global.HTMLCanvasElement &&
        global.HTMLCanvasElement.prototype.transferControlToOffscreen &&
        global.URL &&
        global.URL.createObjectURL);

    function noop() { }

    // create a promise if it exists, otherwise, just
    // call the function directly
    function promise(func) {
        var ModulePromise = module.exports.Promise;
        var Prom = ModulePromise !== void 0 ? ModulePromise : global.Promise;

        if (typeof Prom === 'function') {
            return new Prom(func);
        }

        func(noop, noop);

        return null;
    }

    var raf = (function () {
        var TIME = Math.floor(1000 / 60);
        var frame, cancel;
        var frames = {};
        var lastFrameTime = 0;

        if (typeof requestAnimationFrame === 'function' && typeof cancelAnimationFrame === 'function') {
            frame = function (cb) {
                var id = Math.random();

                frames[id] = requestAnimationFrame(function onFrame(time) {
                    if (lastFrameTime === time || lastFrameTime + TIME - 1 < time) {
                        lastFrameTime = time;
                        delete frames[id];

                        cb();
                    } else {
                        frames[id] = requestAnimationFrame(onFrame);
                    }
                });

                return id;
            };
            cancel = function (id) {
                if (frames[id]) {
                    cancelAnimationFrame(frames[id]);
                }
            };
        } else {
            frame = function (cb) {
                return setTimeout(cb, TIME);
            };
            cancel = function (timer) {
                return clearTimeout(timer);
            };
        }

        return { frame: frame, cancel: cancel };
    }());

    var getWorker = (function () {
        var worker;
        var prom;
        var resolves = {};

        function decorate(worker) {
            function execute(options, callback) {
                worker.postMessage({ options: options || {}, callback: callback });
            }
            worker.init = function initWorker(canvas) {
                var offscreen = canvas.transferControlToOffscreen();
                worker.postMessage({ canvas: offscreen }, [offscreen]);
            };

            worker.fire = function fireWorker(options, size, done) {
                if (prom) {
                    execute(options, null);
                    return prom;
                }

                var id = Math.random().toString(36).slice(2);

                prom = promise(function (resolve) {
                    function workerDone(msg) {
                        if (msg.data.callback !== id) {
                            return;
                        }

                        delete resolves[id];
                        worker.removeEventListener('message', workerDone);

                        prom = null;
                        done();
                        resolve();
                    }

                    worker.addEventListener('message', workerDone);
                    execute(options, id);

                    resolves[id] = workerDone.bind(null, { data: { callback: id } });
                });

                return prom;
            };

            worker.reset = function resetWorker() {
                worker.postMessage({ reset: true });

                for (var id in resolves) {
                    resolves[id]();
                    delete resolves[id];
                }
            };
        }

        return function () {
            if (worker) {
                return worker;
            }

            if (!isWorker && canUseWorker) {
                var code = [
                    'var CONFETTI, SIZE = {}, module = {};',
                    '(' + main.toString() + ')(this, module, true, SIZE);',
                    'onmessage = function(msg) {',
                    '  if (msg.data.options) {',
                    '    CONFETTI(msg.data.options).then(function () {',
                    '      if (msg.data.callback) {',
                    '        postMessage({ callback: msg.data.callback });',
                    '      }',
                    '    });',
                    '  } else if (msg.data.reset) {',
                    '    CONFETTI && CONFETTI.reset();',
                    '  } else if (msg.data.resize) {',
                    '    SIZE.width = msg.data.resize.width;',
                    '    SIZE.height = msg.data.resize.height;',
                    '  } else if (msg.data.canvas) {',
                    '    SIZE.width = msg.data.canvas.width;',
                    '    SIZE.height = msg.data.canvas.height;',
                    '    CONFETTI = module.exports.create(msg.data.canvas);',
                    '  }',
                    '}',
                ].join('\n');
                try {
                    worker = new Worker(URL.createObjectURL(new Blob([code])));
                } catch (e) {
                    // eslint-disable-next-line no-console
                    typeof console !== undefined && typeof console.warn === 'function' ? console.warn('🎊 Could not load worker', e) : null;

                    return null;
                }

                decorate(worker);
            }

            return worker;
        };
    })();

    var defaults = {
        particleCount: 50,
        angle: 90,
        spread: 45,
        startVelocity: 45,
        decay: 0.9,
        gravity: 1,
        drift: 0,
        ticks: 200,
        x: 0.5,
        y: 0.5,
        shapes: ['square', 'circle'],
        zIndex: 100,
        colors: [
            '#26ccff',
            '#a25afd',
            '#ff5e7e',
            '#88ff5a',
            '#fcff42',
            '#ffa62d',
            '#ff36ff'
        ],
        // probably should be true, but back-compat
        disableForReducedMotion: false,
        scalar: 1
    };

    function convert(val, transform) {
        return transform ? transform(val) : val;
    }

    function isOk(val) {
        return !(val === null || val === undefined);
    }

    function prop(options, name, transform) {
        return convert(
            options && isOk(options[name]) ? options[name] : defaults[name],
            transform
        );
    }

    function onlyPositiveInt(number) {
        return number < 0 ? 0 : Math.floor(number);
    }

    function randomInt(min, max) {
        // [min, max)
        return Math.floor(Math.random() * (max - min)) + min;
    }

    function toDecimal(str) {
        return parseInt(str, 16);
    }

    function colorsToRgb(colors) {
        return colors.map(hexToRgb);
    }

    function hexToRgb(str) {
        var val = String(str).replace(/[^0-9a-f]/gi, '');

        if (val.length < 6) {
            val = val[0] + val[0] + val[1] + val[1] + val[2] + val[2];
        }

        return {
            r: toDecimal(val.substring(0, 2)),
            g: toDecimal(val.substring(2, 4)),
            b: toDecimal(val.substring(4, 6))
        };
    }

    function getOrigin(options) {
        var origin = prop(options, 'origin', Object);
        origin.x = prop(origin, 'x', Number);
        origin.y = prop(origin, 'y', Number);

        return origin;
    }

    function setCanvasWindowSize(canvas) {
        canvas.width = document.documentElement.clientWidth;
        canvas.height = document.documentElement.clientHeight;
    }

    function setCanvasRectSize(canvas) {
        var rect = canvas.getBoundingClientRect();
        canvas.width = rect.width;
        canvas.height = rect.height;
    }

    function getCanvas(zIndex) {
        var canvas = document.createElement('canvas');

        canvas.style.position = 'fixed';
        canvas.style.top = '0px';
        canvas.style.left = '0px';
        canvas.style.pointerEvents = 'none';
        canvas.style.zIndex = zIndex;

        return canvas;
    }

    function ellipse(context, x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) {
        context.save();
        context.translate(x, y);
        context.rotate(rotation);
        context.scale(radiusX, radiusY);
        context.arc(0, 0, 1, startAngle, endAngle, antiClockwise);
        context.restore();
    }

    function randomPhysics(opts) {
        var radAngle = opts.angle * (Math.PI / 180);
        var radSpread = opts.spread * (Math.PI / 180);

        return {
            x: opts.x,
            y: opts.y,
            wobble: Math.random() * 10,                                                     // 摇晃
            wobbleSpeed: Math.min(0.11, Math.random() * 0.1 + 0.05),                        // 摆动速度
            velocity: (opts.startVelocity * 0.5) + (Math.random() * opts.startVelocity),    // 速度
            angle2D: -radAngle + ((0.5 * radSpread) - (Math.random() * radSpread)),
            tiltAngle: (Math.random() * (0.75 - 0.25) + 0.25) * Math.PI,
            color: opts.color,
            shape: opts.shape,
            tick: 0,
            totalTicks: opts.ticks,
            decay: opts.decay,                                                               // 衰减
            drift: opts.drift,                                                               // 漂移
            random: Math.random() + 2,
            tiltSin: 0,
            tiltCos: 0,
            wobbleX: 0,
            wobbleY: 0,
            gravity: opts.gravity * 3,                                                       // 重力
            ovalScalar: 0.6,
            scalar: opts.scalar                                                              // 标量的
        };
    }

    function updateFetti(context, fetti) {
        fetti.x += Math.cos(fetti.angle2D) * fetti.velocity + fetti.drift;
        fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity;
        fetti.wobble += fetti.wobbleSpeed;
        fetti.velocity *= fetti.decay;
        fetti.tiltAngle += 0.1;
        fetti.tiltSin = Math.sin(fetti.tiltAngle);
        fetti.tiltCos = Math.cos(fetti.tiltAngle);
        fetti.random = Math.random() + 2;
        fetti.wobbleX = fetti.x + ((10 * fetti.scalar) * Math.cos(fetti.wobble));
        fetti.wobbleY = fetti.y + ((10 * fetti.scalar) * Math.sin(fetti.wobble));

        var progress = (fetti.tick++) / fetti.totalTicks;

        var x1 = fetti.x + (fetti.random * fetti.tiltCos);
        var y1 = fetti.y + (fetti.random * fetti.tiltSin);
        var x2 = fetti.wobbleX + (fetti.random * fetti.tiltCos);
        var y2 = fetti.wobbleY + (fetti.random * fetti.tiltSin);

        context.fillStyle = 'rgba(' + fetti.color.r + ', ' + fetti.color.g + ', ' + fetti.color.b + ', ' + (1 - progress) + ')';
        context.beginPath();

        if (fetti.shape === 'circle') {
            context.ellipse ?
                context.ellipse(fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI) :
                ellipse(context, fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI);
        } else if (fetti.shape === 'star') {
            var rot = Math.PI / 2 * 3;
            var innerRadius = 4 * fetti.scalar;
            var outerRadius = 8 * fetti.scalar;
            var x = fetti.x;
            var y = fetti.y;
            var spikes = 5;
            var step = Math.PI / spikes;

            while (spikes--) {
                x = fetti.x + Math.cos(rot) * outerRadius;
                y = fetti.y + Math.sin(rot) * outerRadius;
                context.lineTo(x, y);
                rot += step;

                x = fetti.x + Math.cos(rot) * innerRadius;
                y = fetti.y + Math.sin(rot) * innerRadius;
                context.lineTo(x, y);
                rot += step;
            }
        } else {
            context.moveTo(Math.floor(fetti.x), Math.floor(fetti.y));
            context.lineTo(Math.floor(fetti.wobbleX), Math.floor(y1));
            context.lineTo(Math.floor(x2), Math.floor(y2));
            context.lineTo(Math.floor(x1), Math.floor(fetti.wobbleY));
        }

        context.closePath();
        context.fill();

        return fetti.tick < fetti.totalTicks;
    }

    function animate(canvas, fettis, resizer, size, done) {
        var animatingFettis = fettis.slice();
        var context = canvas.getContext('2d');
        var animationFrame;
        var destroy;

        var prom = promise(function (resolve) {
            function onDone() {
                animationFrame = destroy = null;

                context.clearRect(0, 0, size.width, size.height);

                done();
                resolve();
            }

            function update() {
                if (isWorker && !(size.width === workerSize.width && size.height === workerSize.height)) {
                    size.width = canvas.width = workerSize.width;
                    size.height = canvas.height = workerSize.height;
                }

                if (!size.width && !size.height) {
                    resizer(canvas);
                    size.width = canvas.width;
                    size.height = canvas.height;
                }

                context.clearRect(0, 0, size.width, size.height);

                animatingFettis = animatingFettis.filter(function (fetti) {
                    return updateFetti(context, fetti);
                });

                if (animatingFettis.length) {
                    animationFrame = raf.frame(update);
                } else {
                    onDone();
                }
            }

            animationFrame = raf.frame(update);
            destroy = onDone;
        });

        return {
            addFettis: function (fettis) {
                animatingFettis = animatingFettis.concat(fettis);

                return prom;
            },
            canvas: canvas,
            promise: prom,
            reset: function () {
                if (animationFrame) {
                    raf.cancel(animationFrame);
                }

                if (destroy) {
                    destroy();
                }
            }
        };
    }

    /**
     * 发射
     * @param {*} canvas 
     * @param {*} globalOpts 
     * @returns Object
     */
    function confettiCannon(canvas, globalOpts) {
        var isLibCanvas = !canvas;
        var allowResize = !!prop(globalOpts || {}, 'resize');
        var globalDisableForReducedMotion = prop(globalOpts, 'disableForReducedMotion', Boolean);
        var shouldUseWorker = canUseWorker && !!prop(globalOpts || {}, 'useWorker');
        var worker = shouldUseWorker ? getWorker() : null;
        var resizer = isLibCanvas ? setCanvasWindowSize : setCanvasRectSize;
        var initialized = (canvas && worker) ? !!canvas.__confetti_initialized : false;
        var preferLessMotion = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion)').matches;
        var animationObj;

        function fireLocal(options, size, done) {
            var particleCount = prop(options, 'particleCount', onlyPositiveInt);
            var angle = prop(options, 'angle', Number);                     // 角度
            var spread = prop(options, 'spread', Number);                   // 传播
            var startVelocity = prop(options, 'startVelocity', Number);     // 启动速度
            var decay = prop(options, 'decay', Number);                     // 衰减
            var gravity = prop(options, 'gravity', Number);                 // 重力
            var drift = prop(options, 'drift', Number);                     // 漂移
            var colors = prop(options, 'colors', colorsToRgb);              // 颜色
            var ticks = prop(options, 'ticks', Number);                     // 发出滴答声
            var shapes = prop(options, 'shapes');                           // 形状
            var scalar = prop(options, 'scalar');                           // 标量的
            var origin = getOrigin(options);                                // 原点

            var temp = particleCount;
            var fettis = [];

            var startX = canvas.width * origin.x;
            var startY = canvas.height * origin.y;

            while (temp--) {
                fettis.push(
                    randomPhysics({
                        x: startX,
                        y: startY,
                        angle: angle,
                        spread: spread,
                        startVelocity: startVelocity,
                        color: colors[temp % colors.length],
                        shape: shapes[randomInt(0, shapes.length)],
                        ticks: ticks,
                        decay: decay,
                        gravity: gravity,
                        drift: drift,
                        scalar: scalar
                    })
                );
            }

            // if we have a previous canvas already animating,
            // add to it
            if (animationObj) {
                return animationObj.addFettis(fettis);
            }

            animationObj = animate(canvas, fettis, resizer, size, done);

            return animationObj.promise;
        }

        function fire(options) {
            var disableForReducedMotion = globalDisableForReducedMotion || prop(options, 'disableForReducedMotion', Boolean);
            var zIndex = prop(options, 'zIndex', Number);

            if (disableForReducedMotion && preferLessMotion) {
                return promise(function (resolve) {
                    resolve();
                });
            }

            if (isLibCanvas && animationObj) {
                // use existing canvas from in-progress animation
                canvas = animationObj.canvas;
            } else if (isLibCanvas && !canvas) {
                // create and initialize a new canvas
                canvas = getCanvas(zIndex);
                document.body.appendChild(canvas);
            }

            if (allowResize && !initialized) {
                // initialize the size of a user-supplied canvas
                resizer(canvas);
            }

            var size = {
                width: canvas.width,
                height: canvas.height
            };

            if (worker && !initialized) {
                worker.init(canvas);
            }

            initialized = true;

            if (worker) {
                canvas.__confetti_initialized = true;
            }

            function onResize() {
                if (worker) {
                    // TODO this really shouldn't be immediate, because it is expensive
                    var obj = {
                        getBoundingClientRect: function () {
                            if (!isLibCanvas) {
                                return canvas.getBoundingClientRect();
                            }
                        }
                    };

                    resizer(obj);

                    worker.postMessage({
                        resize: {
                            width: obj.width,
                            height: obj.height
                        }
                    });
                    return;
                }

                // don't actually query the size here, since this
                // can execute frequently and rapidly
                size.width = size.height = null;
            }

            function done() {
                animationObj = null;

                if (allowResize) {
                    global.removeEventListener('resize', onResize);
                }

                if (isLibCanvas && canvas) {
                    document.body.removeChild(canvas);
                    canvas = null;
                    initialized = false;
                }
            }

            if (allowResize) {
                global.addEventListener('resize', onResize, false);
            }

            if (worker) {
                return worker.fire(options, size, done);
            }

            return fireLocal(options, size, done);
        }

        fire.reset = function () {
            if (worker) {
                worker.reset();
            }

            if (animationObj) {
                animationObj.reset();
            }
        };

        return fire;
    }

    // Make default export lazy to defer worker creation until called.
    var defaultFire;
    function getDefaultFire() {
        if (!defaultFire) {
            defaultFire = confettiCannon(null, { useWorker: true, resize: true });
        }
        return defaultFire;
    }

    module.exports = function () {
        return getDefaultFire().apply(this, arguments);
    };
    module.exports.reset = function () {
        getDefaultFire().reset();
    };
    module.exports.create = confettiCannon;
}((function () {
    if (typeof window !== 'undefined') {
        return window;
    }

    if (typeof self !== 'undefined') {
        return self;
    }

    return this || {};
})(), module, false));

window.confetti = module.exports;

/**
 * 庆祝
 */
function celebrate() {
    var count = 200;
    var defaults = {
        origin: { y: 0.7 }
    };

    function fire(particleRatio, opts) {
        confetti(Object.assign({}, defaults, opts, {
            particleCount: Math.floor(count * particleRatio)
        }));
    }

    fire(0.25, {
        spread: 26,
        startVelocity: 55,
    });

    fire(0.2, {
        spread: 60,
    });
    fire(0.35, {
        spread: 100,
        decay: 0.91,
        scalar: 0.8
    });
    fire(0.1, {
        spread: 120,
        startVelocity: 25,
        decay: 0.92,
        scalar: 1.2
    });
    fire(0.1, {
        spread: 120,
        startVelocity: 45,
    });
}

/**
 * 强烈庆祝
 */
function fireworks(sec=5) {
    var duration = sec * 1000;
    var animationEnd = Date.now() + duration;
    var defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 0 };

    function randomInRange(min, max) {
        return Math.random() * (max - min) + min;
    }

    var interval = setInterval(function () {
        var timeLeft = animationEnd - Date.now();

        if (timeLeft <= 0) {
            return clearInterval(interval);
        }

        var particleCount = 50 * (timeLeft / duration);
        // since particles fall down, start a bit higher than random
        confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } }));
        confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } }));
    }, 250);
}

/**
 * 胜利庆祝
 */
function schoolPride(sec=10) {
    var end = Date.now() + (sec * 1000);

    // go Buckeyes!
    var colors = ['#bb0000', '#ffffff'];

    (function frame() {
        confetti({
            particleCount: 2,
            angle: 60,
            spread: 55,
            origin: { x: 0 },
            colors: colors
        });
        confetti({
            particleCount: 2,
            angle: 120,
            spread: 55,
            origin: { x: 1 },
            colors: colors
        });
        if (Date.now() < end) {
            requestAnimationFrame(frame);
        }
    }());
}